package com.example.pacs.dicom.server.cmove;

import com.example.pacs.bean.request.QueryParamsRequest;
import com.example.pacs.dicom.server.cstorescu.CSTORESCU;
import com.example.pacs.service.DicomServerService;
import org.dcm4che3.data.Attributes;
import org.dcm4che3.data.Tag;
import org.dcm4che3.data.UID;
import org.dcm4che3.net.*;
import org.dcm4che3.net.pdu.AAssociateRQ;
import org.dcm4che3.net.pdu.PresentationContext;
import org.dcm4che3.net.service.BasicCMoveSCP;
import org.dcm4che3.net.service.BasicRetrieveTask;
import org.dcm4che3.net.service.DicomServiceException;
import org.dcm4che3.net.service.RetrieveTask;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;

import javax.annotation.Resource;
import java.util.Iterator;
import java.util.List;

/**
 * C-MOVE服务端实现
 */
@Component
public class CMOVESUP extends BasicCMoveSCP {

    /**
     * 填充这个SUP提供的功能(SOP CLASS UID)
     */
    public CMOVESUP() {
        super(UID.ModalityWorklistInformationModelFIND, UID.StudyRootQueryRetrieveInformationModelMOVE);
    }

    @Resource
    private CSTORESCU cstorescu;

    @Resource
    private DicomServerService dicomServerService;

    private ApplicationEntity localAE;

    private ApplicationEntity destAE;

    private List<InstanceLocators> instanceLocators;

    @Override
    protected RetrieveTask calculateMatches(Association as, PresentationContext pc, Attributes rq, Attributes keys) throws DicomServiceException {
        // 获取查询参数
        QueryParamsRequest params = QueryParamsRequest.builder()
                .patientId(keys.getString(Tag.PatientID))
                .patientName(keys.getString(Tag.PatientName))
                .modalityType(keys.getString(Tag.Modality))
                .startStudyDateAndTime(keys.getDate(Tag.ScheduledStudyStartDateAndTime))
                .endStudyDateAndTime(keys.getDate(Tag.ScheduledStudyStopDateAndTime))
                .build();
        ArchiveAEExtension aeExtension = as.getApplicationEntity().getAEExtension(ArchiveAEExtension.class);
        // 从数据库查询DICOM文件
        this.instanceLocators = dicomServerService.cmoveQuery(params);
        // 获取发送c-move方aet
        String destinationAE = rq.getString(Tag.MoveDestination);
        // 获取对方的应用
        this.destAE = new ApplicationEntity(destinationAE);
        // 获取本地的应用
        this.localAE = aeExtension.getApplicationEntity();
        // 获取一个连接
        Association storeas = openAssociation();
        return new BasicRetrieveTask<>(Dimse.C_MOVE_RQ, as, pc, rq, instanceLocators, storeas, cstorescu);
    }

    private Association openAssociation() throws DicomServiceException {
        // 开始连接
        try {
            Association storeas = localAE.connect(destAE, creatAssociationRQ());
            instanceLocators.removeIf(instanceLocators -> storeas.getTransferSyntaxesFor(instanceLocators.getSopClassUID()).isEmpty());
            return storeas;
        } catch (Exception e) {
            throw new DicomServiceException(Status.UnableToPerformSubOperations, e);
        }
    }

    private AAssociateRQ creatAssociationRQ() {
        AAssociateRQ aAssociateRQ = new AAssociateRQ();
        if (!localAE.isAcceptedCallingAETitle(destAE.getAETitle())) {
            aAssociateRQ.setCallingAET(localAE.getAETitle());
        }
        boolean empty = destAE.getTransferCapabilitiesWithRole(TransferCapability.Role.SCP).isEmpty();
        if (!CollectionUtils.isEmpty(instanceLocators)) {
            Iterator<InstanceLocators> iterator = instanceLocators.iterator();
            while (iterator.hasNext()) {
                InstanceLocators instanceLocators = iterator.next();
                String cuid = instanceLocators.getSopInstanceUID();
                TransferCapability localTransferCapabilityFor = localAE.getTransferCapabilityFor(cuid, TransferCapability.Role.SCP);
                TransferCapability destTransferCapability = empty ? null : destAE.getTransferCapabilityFor(cuid, TransferCapability.Role.SCP);
                if (!aAssociateRQ.containsPresentationContextFor(cuid) && isVideo(instanceLocators)) {
                    if (empty) {
                        addPresentationContext(aAssociateRQ, cuid, UID.ImplicitVRLittleEndian, localTransferCapabilityFor);
                        addPresentationContext(aAssociateRQ, cuid, UID.ExplicitVRLittleEndian, localTransferCapabilityFor);
                    } else {
                        addPresentationContext(aAssociateRQ, cuid, UID.ImplicitVRLittleEndian, localTransferCapabilityFor, destTransferCapability);
                        addPresentationContext(aAssociateRQ, cuid, UID.ExplicitVRLittleEndian, localTransferCapabilityFor, destTransferCapability);
                    }
                }
            }
        }
        return aAssociateRQ;
    }

    private void addPresentationContext(AAssociateRQ aAssociateRQ, String cuid, String transferSyntax, TransferCapability localTrans, TransferCapability destTrans) {
        if (localTrans != null && localTrans.containsTransferSyntax(transferSyntax)) {
            addPresentationContext(aAssociateRQ, cuid, transferSyntax, destTrans);
        }
    }

    private void addPresentationContext(AAssociateRQ aAssociateRQ, String cuid, String transferSyntax, TransferCapability transferCapability) {
        if (transferCapability != null && transferCapability.containsTransferSyntax(transferSyntax)) {
            aAssociateRQ.addPresentationContextFor(cuid, transferSyntax);
        }
    }

    private boolean isVideo(InstanceLocators inst) {
        switch (inst.getTransferSyntaxUid()) {
            case UID.MPEG2:
            case UID.MPEG2MainProfileHighLevel:
            case UID.MPEG4AVCH264BDCompatibleHighProfileLevel41:
            case UID.MPEG4AVCH264HighProfileLevel41:
            case UID.MPEG4AVCH264HighProfileLevel42For2DVideo:
            case UID.MPEG4AVCH264HighProfileLevel42For3DVideo:
            case UID.MPEG4AVCH264StereoHighProfileLevel42:
            case UID.HEVCH265Main10ProfileLevel51:
            case UID.HEVCH265MainProfileLevel51:
                return true;
        }
        return false;
    }
}
